package stdio.costa.moca.pdf;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

import stdio.costa.moca.PostcardHandler;
import stdio.costa.moca.model.Address;
import stdio.costa.moca.model.Img;
import stdio.costa.moca.model.Postcard;
import stdio.costa.utils.IOUtils;

import com.aspose.words.Document;
import com.aspose.words.DocumentBuilder;
import com.aspose.words.ImportFormatMode;
import com.aspose.words.License;
import com.aspose.words.NodeType;
import com.aspose.words.SaveFormat;
import com.aspose.words.SectionStart;
import com.aspose.words.Shape;
import com.aspose.words.Table;

public class AsposePdfPostcardHandler implements PostcardHandler {

	public AsposePdfPostcardHandler() {
		loadLicense();
	}

	@Override
	public Postcard handle(Postcard postcard) throws Exception {
		Document front = getFrontDocument(postcard);
		Document back = getBackDocument(postcard);
		back.getFirstSection().getPageSetup().setSectionStart(SectionStart.NEW_PAGE);
		front.appendDocument(back, ImportFormatMode.KEEP_SOURCE_FORMATTING);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		front.save(baos, SaveFormat.PDF);
		OutputStream out = new FileOutputStream("/Users/guilinzhang/Documents/files/test.pdf");
		out.write(baos.toByteArray());
		out.close();
		postcard.setPdfGenerated(true);
		return postcard;
	}

	protected Document getFrontDocument(Postcard postcard) throws Exception {
		Document doc = new Document("/Users/guilinzhang/Documents/files/front.doc");
		DocumentBuilder builder = new DocumentBuilder(doc);
		Shape shape = builder.insertImage(postcard.getPhoto().getData());
		shape.setHeight(builder.getPageSetup().getPageHeight());
		shape.setWidth(builder.getPageSetup().getPageWidth());
		return doc;
	}
	
	protected Document getBackDocument(Postcard postcard) throws Exception {
		Document doc = new Document("/Users/guilinzhang/Documents/files/back.doc");
		DocumentBuilder builder = new DocumentBuilder(doc);
		addReceiverAddress(doc, postcard.getAddress());
		addSenderAddress(doc, postcard.getSender().getAddress());
		addWishAndStamp(doc, builder, postcard.getWish(), postcard.getStamp());
		return doc;
	}

	protected void addWishAndStamp(Document doc, DocumentBuilder builder, Img wish, Img stamp) throws Exception {
		Table table = (Table) doc.getChild(NodeType.TABLE, 0, true);
		TableVisitableDecorator visitable = new TableVisitableDecorator(table);
		visitable.accept(new ReplaceTextWithImageInCellVisitor("$wish", wish.getData(), builder));
		visitable.accept(new ReplaceTextWithImageInNestedCellVisitor("$stamp", stamp.getData(), builder));
	}

	protected void addSenderAddress(Document doc, Address senderAddress) throws Exception {
		doc.getRange().replace("$sender", StringUtils.trimToEmpty(senderAddress.getReceiver()), true, false);
		doc.getRange().replace("$sProvince", StringUtils.trimToEmpty(senderAddress.getProvince()), true, false);
		doc.getRange().replace("$sCity", StringUtils.trimToEmpty(senderAddress.getCity()), true, false);
		doc.getRange().replace("$sDistrict", StringUtils.trimToEmpty(senderAddress.getDistrict()), true, false);
		doc.getRange().replace("$sAddressLine1", StringUtils.trimToEmpty(senderAddress.getAddressLine1()), true, false);
		doc.getRange().replace("$sAddressLine2", StringUtils.trimToEmpty(senderAddress.getAddressLine2()), true, false);
		addPostcode(doc, senderAddress, "$sp");
	}

	protected void addPostcode(Document doc, Address address, String key) throws Exception {
		char[] chars = address.getPostcode().toCharArray();
		for(int i = 0; i < chars.length; i++) {
			int n = i + 1;
			doc.getRange().replace(key + n, String.valueOf(chars[i]), true, false);
		}
	}

	protected void addReceiverAddress(Document doc, Address receiverAddress) throws Exception {
		doc.getRange().replace("$receiver", StringUtils.trimToEmpty(receiverAddress.getReceiver()), true, false);
		doc.getRange().replace("$province", StringUtils.trimToEmpty(receiverAddress.getProvince()), true, false);
		doc.getRange().replace("$city", StringUtils.trimToEmpty(receiverAddress.getCity()), true, false);
		doc.getRange().replace("$district", StringUtils.trimToEmpty(receiverAddress.getDistrict()), true, false);
		doc.getRange().replace("$addressLine1", StringUtils.trimToEmpty(receiverAddress.getAddressLine1()), true, false);
		doc.getRange().replace("$addressLine2", StringUtils.trimToEmpty(receiverAddress.getAddressLine2()), true, false);
		addPostcode(doc, receiverAddress, "$p");
	}

	protected void loadLicense() {
		InputStream in = getClass().getResourceAsStream("Moca.LIC");
		License license = new License();
		try {
			license.setLicense(new ByteArrayInputStream(Base64.decodeBase64(IOUtils.read(in))));
		} catch (Exception e) {
			throw new IllegalStateException("Failed to initialize Aspose license.");
		} finally {
			IOUtils.close(in);
		}
	}
}
